#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests_textid_formatters
{		
    using namespace boost::assign;
    namespace fs = std::tr2::sys;

    TEST_CLASS(boost_string_algorithms)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"boost_string_algorithms")
            END_TEST_CLASS_ATTRIBUTE();

        std::map<std::string, std::string> m_regexes;

        std::string load_regex(std::string const & p_regex_name)
        {
            std::stringstream t_out;
            t_out 
                << "id: " << p_regex_name << std::endl
                << "regex: " << m_regexes[p_regex_name] << std::endl;
            Logger::WriteMessage(t_out.str().c_str());

            return m_regexes[p_regex_name];
        }

        template <class TIn, class TExpItr, class TOut>
        void write_result(
            TIn const & p_input, 
            TExpItr p_exp_beg, 
            TExpItr p_exp_end, 
            TOut const & p_out)
        {
            std::stringstream t_out;
            t_out << "input: " << p_input << std::endl;

            int t_id = 0;
            t_out << "expectation: " << std::endl;
            for (; p_exp_beg != p_exp_end; ++p_exp_beg) {
                t_out << "token[" << t_id++ << "]: " << *p_exp_beg << std::endl;
            }

            t_id = 0;
            t_out << "result: " << std::endl;
            for (auto t_token : p_out) {
                t_out << "token[" << t_id++ << "]: " << t_token << std::endl;
            }
            Logger::WriteMessage(t_out.str().c_str());
        }

        template <class TIn, class TExp, class TOut>
        void write_result(TIn const & p_input, TExp const & p_exp, TOut const & p_out)
        {
            return this->write_result(p_input, p_exp.begin(), p_exp.end(), p_out);
        }

        template <class TCharItr>
        class split_regex_token_iterator : public boost::iterator_facade<
            split_regex_token_iterator<TCharItr>, 
            std::sub_match<TCharItr> const,
            typename std::iterator_traits< std::regex_iterator<TCharItr> >::iterator_category
        > 
        {
            enum state {
                undefined,
                showing_prefix,
                showing_match,
                showing_suffix
            };

            friend class boost::iterator_core_access;

            typedef split_regex_token_iterator<TCharItr> _this_type;
            typedef std::sub_match<TCharItr> const _value_type;
            typedef std::sub_match<TCharItr> const & _reference_type;

            std::regex_iterator<TCharItr> m_current_itr;
            _value_type m_current_match;
            state m_state;

            void increment()
            {
                switch (m_state) {
                case state::showing_prefix:
                    _value_type(m_current_itr->position(), m_current_itr->position() + m_current_itr->length())
                        .swap(m_current_match);
                    m_state = state::showing_match;
                case state::showing_match:

                case state::showing_suffix:
                default:
                }
            }

            _reference_type dereference() const
            {
                switch (m_state) {
                case _this_type::showing_prefix:
                    return m_current_itr->prefix();
                case _this_type::showing_match:
                    return m_current_match;
                case _this_type::showing_suffix:
                    return m_current_itr->suffix();
                default:
                    return m_current_match;
                }
            }

            bool equal(split_regex_token_iterator const & p_other) const
            {
                return 
                    m_state == p_other.m_state &&
                    m_current_itr == p_other.m_current_itr;
            }
        public:
            split_regex_token_iterator(
                TCharItr p_beg, 
                TCharItr p_end, 
                std::basic_regex<typename TCharItr> const p_regex) 
                : m_current_itr(p_beg, p_end, p_regex)
                , m_state(_this_type::showing_prefix) 
            {

            }

            split_regex_token_iterator() 
                : m_state(_this_type::undefined) {}
        };

        template<typename TSequence, typename TRegexItr> 
        TSequence & split_regex_incl_delimiters(
            TSequence & p_result, 
            TRegexItr p_regex_beg,
            TRegexItr p_regex_end)
        {
            TRegexItr t_regex_last;
            typedef typename TSequence::value_type seq_value_t;
            for (; p_regex_beg != p_regex_end; ++p_regex_beg) {
                if (p_regex_beg->prefix().length()) {
                    p_result.push_back(seq_value_t(p_regex_beg->prefix().first, p_regex_beg->prefix().second));
                }
                if (!p_regex_beg->empty()) {
                    p_result.push_back(p_regex_beg->str());
                }
                t_regex_last = p_regex_beg;
            }

            if (t_regex_last != p_regex_end) {
                if (t_regex_last->suffix().length()) {
                    p_result.push_back(seq_value_t(t_regex_last->suffix().first, t_regex_last->suffix().second));
                }
            }
            return p_result;
        }

        template<typename TSequence, typename TTrimFunc>
        TSequence& trim(TSequence& p_input, TTrimFunc p_trim_func)
        {
            for (auto & t_token : p_input) {
                p_trim_func(t_token);
            }
            return p_input;
        }


        //std::string load_regex(std::string const & p_regex_name)
        //{
        //    auto t_path = fs::system_complete(fs::path("../../../../tests.data/tests.regexes.txt"));
        //    Logger::WriteMessage( std::string(t_path.string()).append("\n").c_str() );
        //    Assert::IsTrue(fs::exists(t_path));

        //    std::ifstream t_file(t_path);
        //    Assert::IsTrue(t_file.good());

        //    std::string t_line;
        //    while(std::getline(t_file, t_line).good()) {
        //        Logger::WriteMessage( std::string(t_line).append("\n").c_str() );

        //        boost::tokenizer<boost::escaped_list_separator<char> > t_tokenizer(t_line);
        //        std::vector<std::string> t_tokens;
        //        std::copy(t_tokenizer.begin(), t_tokenizer.end(), std::back_inserter(t_tokens));

        //        Assert::AreEqual(2u, t_tokens.size());

        //        Logger::WriteMessage( std::string("id: ")
        //            .append(t_tokens.front())
        //            .append("\n").c_str() );
        //        Logger::WriteMessage( std::string("regex: ")
        //            .append(t_tokens.back())
        //            .append("\n").c_str() );

        //        if (2u == t_tokens.size() && t_tokens.front() == p_regex_name) {
        //            return t_tokens.back();
        //        }
        //    }

        //    return std::string();
        //}
    public:

        TEST_METHOD_INITIALIZE(set_up)
        {
            m_regexes["literal_plural_regex"] =
                "(\"(?:[^\\\\\"]|\\\\.)*\")|(Plural[\\s]*\\([\\s]*\\))";
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
        }

        TEST_METHOD(boost_match_literal_plural_regex)
        {
            namespace rgx = boost;

            std::vector<std::string> t_inputs;
            t_inputs +=
                "Localize(\"You have {1} message\", \"You have {1} messages\", Plural())",
                "  Localize ( \"You have {1} message\" )",
                "",
                "    Localize()",
                "Localize(\"You have {1} message\", \"You have {1} messages\")";

            std::vector< std::vector<std::string> > t_exps;
            {
                std::vector<std::string> t_exp;
                t_exp +=
                    "\"You have {1} message\"",
                    "\"You have {1} messages\"",
                    "Plural()";
                t_exps += t_exp;
            }
            {
                std::vector<std::string> t_exp;
                t_exp +=
                    "\"You have {1} message\"";
                t_exps += t_exp;
            }
            { // empty!
                std::vector<std::string> t_exp;
                t_exps += t_exp;
            }
            { // empty!
                std::vector<std::string> t_exp;
                t_exps += t_exp;
            }
            {
                std::vector<std::string> t_exp;
                t_exp +=
                    "\"You have {1} message\"",
                    "\"You have {1} messages\"";
                t_exps += t_exp;
            }

            std::string t_regex_exp = this->load_regex("literal_plural_regex");
            Assert::IsFalse(t_regex_exp.empty());

            Assert::AreEqual(t_exps.size(), t_inputs.size());
            auto t_exp = t_exps.begin();
            for (auto t_input : t_inputs) {
                rgx::regex t_regex(t_regex_exp);
                rgx::sregex_iterator t_beg(t_input.begin(), t_input.end(), t_regex);
                rgx::sregex_iterator t_end;

                std::vector<std::string> t_res;
                for (; t_beg != t_end; ++t_beg) {
                    if (!t_beg->empty()) {
                        t_res.push_back(t_beg->str());
                    }
                }

                this->write_result(t_input, t_exp->begin(), t_exp->end(), t_res);
                Assert::AreEqual(t_exp->size(), t_res.size());
                Assert::IsTrue(std::equal(t_exp->begin(), t_exp->end(), t_res.begin()));
                ++t_exp;
            }
        }

        TEST_METHOD(std_match_literal_plural_regex)
        {
            namespace rgx = std;

            std::vector<std::string> t_inputs;
            t_inputs +=
                "Localize(\"You have {1} message\", \"You have {1} messages\", Plural())",
                "  Localize ( \"You have {1} message\" )",
                "",
                "    Localize()",
                "Localize(\"You have {1} message\", \"You have {1} messages\")";

            std::vector< std::vector<std::string> > t_exps;
            {
                std::vector<std::string> t_exp;
                t_exp +=
                    "\"You have {1} message\"",
                    "\"You have {1} messages\"",
                    "Plural()";
                t_exps += t_exp;
            }
            {
                std::vector<std::string> t_exp;
                t_exp +=
                    "\"You have {1} message\"";
                t_exps += t_exp;
            }
            { // empty!
                std::vector<std::string> t_exp;
                t_exps += t_exp;
            }
            { // empty!
                std::vector<std::string> t_exp;
                t_exps += t_exp;
            }
            {
                std::vector<std::string> t_exp;
                t_exp +=
                    "\"You have {1} message\"",
                    "\"You have {1} messages\"";
                t_exps += t_exp;
            }

            std::string t_regex_exp = this->load_regex("literal_plural_regex");
            Assert::IsFalse(t_regex_exp.empty());

            Assert::AreEqual(t_exps.size(), t_inputs.size());
            auto t_exp = t_exps.begin();
            for (auto t_input : t_inputs) {
                rgx::regex t_regex(t_regex_exp);
                rgx::sregex_iterator t_beg(t_input.begin(), t_input.end(), t_regex);
                rgx::sregex_iterator t_end;

                std::vector<std::string> t_res;
                for (; t_beg != t_end; ++t_beg) {
                    if (!t_beg->empty()) {
                        t_res.push_back(t_beg->str());
                    }
                }

                this->write_result(t_input, t_exp->begin(), t_exp->end(), t_res);
                Assert::AreEqual(t_exp->size(), t_res.size());
                Assert::IsTrue(std::equal(t_exp->begin(), t_exp->end(), t_res.begin()));
                ++t_exp;
            }
        }

        TEST_METHOD(boost_split_literal_plural_regex)
        {
            namespace rgx = boost;

            const std::string t_input(
                "Localize(\"You have {1} message\", \"You have {1} messages\", Plural())");

            std::vector<std::string> t_exp;
            t_exp +=
                "Localize(",
                "\"You have {1} message\"",
                ",", 
                "\"You have {1} messages\"",
                ",",
                "Plural()",
                ")";

            std::string t_regex_exp = this->load_regex("literal_plural_regex");
            Assert::IsFalse(t_regex_exp.empty());

            std::vector<std::string> t_res;
            rgx::regex t_regex(t_regex_exp);
            this->trim(this->split_regex_incl_delimiters(
                t_res,
                rgx::sregex_iterator(t_input.begin(), t_input.end(), t_regex),
                rgx::sregex_iterator()),
                [] (std::string & p_token) -> std::string & { rgx::trim(p_token); return p_token; });

            this->write_result(t_input, t_exp, t_res);

            Assert::AreEqual(t_exp.size(), t_res.size());
            Assert::IsTrue(std::equal(t_exp.begin(), t_exp.end(), t_res.begin()));
        }

        TEST_METHOD(std_split_literal_plural_regex)
        {
            namespace rgx = std;

            const std::string t_input(
                "Localize(\"You have {1} message\", \"You have {1} messages\", Plural())");

            std::vector<std::string> t_exp;
            t_exp +=
                "Localize(",
                "\"You have {1} message\"",
                ",", 
                "\"You have {1} messages\"",
                ",",
                "Plural()",
                ")";

            std::string t_regex_exp = this->load_regex("literal_plural_regex");
            Assert::IsFalse(t_regex_exp.empty());

            std::vector<std::string> t_res;
            rgx::regex t_regex(t_regex_exp);
            this->trim(this->split_regex_incl_delimiters(
                t_res,
                rgx::sregex_iterator(t_input.begin(), t_input.end(), t_regex),
                rgx::sregex_iterator()),
                [] (std::string & p_token) -> std::string & { boost::trim(p_token); return p_token; });

            this->write_result(t_input, t_exp, t_res);

            Assert::AreEqual(t_exp.size(), t_res.size());
            Assert::IsTrue(std::equal(t_exp.begin(), t_exp.end(), t_res.begin()));
        }
    };
}